package com.wujunshen.thread.lock.fairlock;

import java.util.ArrayList;
import java.util.List;

/**
 * @author frank woo(吴峻申) <br>
 *     email:<a href="mailto:frank_wjs@hotmail.com">frank_wjs@hotmail.com</a> <br>
 * @date 2020/7/18 11:41<br>
 */
public class FairLock {
  private final List<QueueObject> waitingThreads = new ArrayList<>();
  private boolean isLocked = false;
  private Thread lockingThread = null;

  public void lock() throws InterruptedException {
    QueueObject queueObject = new QueueObject();
    boolean isLockedForThisThread = true;
    synchronized (this) {
      waitingThreads.add(queueObject);
    }
    while (isLockedForThisThread) {
      synchronized (this) {
        isLockedForThisThread = isLocked || waitingThreads.get(0) != queueObject;
        if (!isLockedForThisThread) {
          isLocked = true;
          waitingThreads.remove(queueObject);
          lockingThread = Thread.currentThread();
          return;
        }
      }
      try {
        queueObject.doWait();
      } catch (InterruptedException e) {
        synchronized (this) {
          waitingThreads.remove(queueObject);
        }
        throw e;
      }
    }
  }

  public synchronized void unlock() {
    if (this.lockingThread != Thread.currentThread()) {
      throw new IllegalMonitorStateException("Calling thread has not locked this lock");
    }
    isLocked = false;
    lockingThread = null;
    if (waitingThreads.size() > 0) {
      waitingThreads.get(0).doNotify();
    }
  }
}
